<template>
	<view class="load-refresh">
		<!-- 刷新动画，可自定义，占高100rpx -->
		<view class="animation" :style="{'--color': color}">
			<view v-if="pullDown && state!=2" class="remind"> {{moving ? '松开立即刷新' : '继续下拉刷新'}} </view>
			<view v-if="pullDown && state==2" class="refresh">
				<xl-loading :type="loadingType" :size="30" :color="color"></xl-loading>
			</view>
		</view>
		<!-- 数据列表块 -->
		<view
			class="cover-container"
			:style="[{
				background: background,
				transform: coverTransform,
				transition: coverTransition
			}]"
			@touchstart="coverTouchstart"
			@touchmove="coverTouchmove"
			@touchend="coverTouchend">
			<view v-if="!fixed" class="list">
				<!-- 数据集插槽 -->
				<view v-if="state!==4" id="waterfall_flow" class="waterfall-flow" :style="{'--padding-top':$xl.calcUnit(top, 'rpx'), '--padding-sides':$xl.calcUnit(sides, 'rpx'),'--col':columns,'--row-gap':$xl.calcUnit(rowGap, 'rpx'),'--col-gap':$xl.calcUnit(colGap, 'rpx')}">
					<!-- #ifdef MP-WEIXIN -->
					<grid-view type="masonry" :cross-axis-count="columns" :main-axis-gap="$xl.upx2px(rowGap)" :cross-axis-gap="$xl.upx2px(colGap)">
						<view v-for="(item, index) in dataList" :key="index" :class="{'item': true, 'shadow': itemShadow}" :style="{borderRadius:$xl.calcUnit(itemRadius, 'rpx')}" @click="clickItem(item)">
							<xl-image :src="item[imageName]" mode="widthFix" @click="clickImage(item)"></xl-image>
							<slot name="item" :item="item"></slot>
						</view>
					</grid-view>
					<!-- #endif -->		
					<!-- #ifndef MP-WEIXIN -->
					<view v-for="(item,index) in dataList" :key="index" :class="{'item': true, 'shadow': itemShadow}" 
						:style="{borderRadius:$xl.calcUnit(itemRadius, 'rpx'), 'grid-row':`span ${item.spanRows?item.spanRows:0}`}" 
						@click="clickItem(item)">
						<xl-image :src="item[imageName]" mode="widthFix" @finish="e => loadImage(e, item, index)" :height="item.imageHeight ? item.imageHeight:'0px'" @click="clickImage(item)"></xl-image>
						<view :id="'itemInfo'+index">
							<slot name="item" :item="item"></slot>
						</view>
					</view>
					<!-- #endif -->
				</view>
				<slot v-if="state==4" name="empty">
					<xl-empty :text="emptyMsg" color="#888" :top="emptyTop"></xl-empty>
				</slot>
				<!-- 提示信息 -->
				<xl-divider-tip v-else-if="state!=3 || (state==3 && showNoMore)" :loading="state==1" width="80%" :fontSize="14" textColor="#aaa" :lineColor="['#fff', '#aaa']" :backgroundColor="background">{{stateText}}</xl-divider-tip>
			</view>
			<scroll-view v-else scroll-y class="list" :scroll-top="scrollTop" @scrolltolower="loadMore" :style="[scrollHeight]">
				<!-- 数据集插槽 -->
				<view v-if="state!==4" id="waterfall_flow" class="waterfall-flow" :style="{'--padding-top':$xl.calcUnit(top, 'rpx'), '--padding-sides':$xl.calcUnit(sides, 'rpx'),'--col':columns,'--row-gap':$xl.calcUnit(rowGap, 'rpx'),'--col-gap':$xl.calcUnit(colGap, 'rpx')}">
					<!-- #ifdef MP-WEIXIN -->
					<grid-view type="masonry" :cross-axis-count="columns" :main-axis-gap="$xl.upx2px(rowGap)" :cross-axis-gap="$xl.upx2px(colGap)">
						<view v-for="(item, index) in dataList" :key="index" :class="{'item': true, 'shadow': itemShadow}" :style="{borderRadius:$xl.calcUnit(itemRadius, 'rpx')}" @click="clickItem(item)">
							<xl-image :src="item[imageName]" mode="widthFix" @click="clickImage(item)"></xl-image>
							<slot name="item" :item="item"></slot>
						</view>
					</grid-view>
					<!-- #endif -->		
					<!-- #ifndef MP-WEIXIN -->
					<view v-for="(item,index) in dataList" :key="index" :class="{'item': true, 'shadow': itemShadow}" 
						:style="{borderRadius:$xl.calcUnit(itemRadius, 'rpx'), 'grid-row':`span ${item.spanRows?item.spanRows:0}`}" 
						@click="clickItem(item)">
						<xl-image :src="item[imageName]" mode="widthFix" @finish="e => loadImage(e, item, index)" :height="item.imageHeight ? item.imageHeight:'0px'" @click="clickImage(item)"></xl-image>
						<view :id="'itemInfo'+index">
							<slot name="item" :item="item"></slot>
						</view>
					</view>
					<!-- #endif -->
				</view>
				<slot v-if="state==4" name="empty">
					<xl-empty :text="emptyMsg" color="#888" :top="emptyTop"></xl-empty>
				</slot>
				<!-- 提示信息 -->
				<xl-divider-tip v-else-if="state!=3 || (state==3 && showNoMore)" :loading="state==1" width="80%" :fontSize="14" textColor="#aaa" :lineColor="['#fff', '#aaa']" :backgroundColor="background">{{stateText}}</xl-divider-tip>
			</scroll-view>
		</view>
	</view>
</template>

<script setup>
	import { ref, reactive, computed, onMounted, watch,getCurrentInstance } from 'vue'
	import { onPageScroll, onReachBottom  } from "@dcloudio/uni-app";
	// 组件传参
	import propsData from './props.js'
	const props = defineProps(propsData)
	// 组件触发方法
	const emits = defineEmits(['provider', 'initLoad', 'reload', 'reachBottom', 'clickItem', 'clickImage'])
	// 提供给父组件进行ref来切换
	defineExpose({ initLoad, reload, loadMore, push, getData, addData, updateData, deleteData })
	
	// 计算组件高度
	const scrollHeight = computed(()=>{
		if(props.fixed){
			if (Number(props.fixedHeight)) {
				let height = uni.$xl.parseUnit(props.fixedHeight, 'rpx');
				return `height: ${height};`
			} else {
				let height = uni.getWindowInfo().screenHeight - uni.getWindowInfo().statusBarHeight - uni.$xl.upx2px(80 + props.reduceHeight)
				return `height: ${height}px; -webkit-overflow-scrolling:touch;`
			}
		}else{
			return "";
		}
	})
	
	// 计算瀑布流一列的宽度
	const columnWidth = computed(()=>{
		if(props.waterfallWidth){
			return (props.waterfallWidth - uni.$xl.upx2px(props.colGap) * (props.columns - 1) - uni.$xl.upx2px(props.sides) * 2) / props.columns;
		}else{
			return (uni.getWindowInfo().windowWidth - uni.$xl.upx2px(props.colGap) * (props.columns - 1) - uni.$xl.upx2px(props.sides) * 2) / props.columns;
		}
	})
	// 瀑布流grid网格布局中，一个item元素的行跨度，即高度
	const instance = getCurrentInstance();
	function loadImage(e, item, index) {
		const rate = e.detail.width / e.detail.height;
		const imgHeight = columnWidth.value / rate;
		uni.createSelectorQuery().in(instance.proxy).select('#itemInfo'+index).boundingClientRect((data) => {
			item.spanRows = Math.ceil(imgHeight) + Math.ceil(data.height);
			item.imageHeight = `${imgHeight}px`
		}).exec();
	}
	function clickItem(item){
		emits("clickItem", item);
	}
	function clickImage(item){
		emits("clickImage", item);
	}
	
	
	// 触发父组件进行加载数据
	function provide(){
		emits('provider', {
			pageNum: currentPage,
			pageSize: props.pageSize
		}, push)
	}
	
	let currentPage = 1  // 当前页
	const dataList = ref([]) // 数据列表
	const state = ref(0)  // 状态：0、上拉加载更多，1、加载中，2、刷新中，3、暂时没有数据，4、已经到底啦~
	// 判断loadText，可以根据需求自定义
	const stateText = computed(()=>{
		var text = '';
		switch(state.value){
			case 0:text = '上拉加载更多';break;
			case 1:text = '加载中';break;
			case 2:text = '更新中';break;
			case 2.5:text = '更新成功';break;
			case 3:text = '已经到底啦';break;
			case 4:text = '暂时没有数据';break;
			case 5:text = '发生错误，请重试';break;
			default:text = '没有更多内容';break;
		}
		return text;
	})
	
	// 初始化的时候，会执行一次加载
	onMounted(() => {
	    if(!props.lazyLoad){
	    	// 加载中
	    	state.value = 1;
	    	provide();
	    }
	})
	
	let firstLoad = true    // 是否第一次加载，每次切换的时候来判断是否第一次，是则加载，不是则不加载
	watch( () => props.load, (newValue, oldValue) => {
		if(newValue) initLoad()
	})
	// 若为懒加载，不使用init字段控制的话，则可以使用这个方法来初始化加载数据，第二次使用时，会判断之前是否已经初始化过了
	function initLoad(){
		if(firstLoad){
			firstLoad = false;
			reload();
			emits("initLoad");
		}else if(props.refresh){
			// 如果再显示时，可以刷新，则刷新数据
			reload();
			emits("reload");
		}
	}
	// 重新加载数据
	function reload(){
		if(state.value === 1 || state.value === 2) return;
		state.value = 1;
		
		dataList.value = []
		currentPage = 1
		provide();
	}
	// scroll-view上划会触发加载更多数据loadMore
	function loadMore() {
		// 只有在可上划加载的状态才能执行加载
		if(state.value !== 0 ) return;
		// 请求下一页
		currentPage++;
		// 加载中
		state.value = 1;
		provide();
		emits("reachBottom");
	}
	// 不是固定窗口，用页面触底方法onReachBottom
	if(!props.fixed){
		onReachBottom(() => {
			loadMore()
		})
	}
	
	let moving = ref(false)
	let scrollTop = ref(-1)
	let coverTransform = ref('translateY(0px)')
	let coverTransition = ref('0s')
	// 更新数据
	function push(arr){
		// 如果是下拉刷新，需要先动画处理，再添加数据
		if(state.value == 2){
			moving.value = false
			scrollTop.value = -1
			coverTransform.value = 'translateY(0px)';
			coverTransition.value = 'transform 0.3s linear';
			state.value = 2.5;
			setTimeout(() => {
				// 清空原来的数据
				dataList.value = []
				// 刷新数据
				pushData(arr);
			}, 500)
		}else{
			// 如果不是下拉刷新，则直接添加
			pushData(arr);
		}
	}
	// 向列表中添加数据，并更新状态
	function pushData(arr){
		// 有数据push了，不管是否为空，都表示已经加载过一次了
		firstLoad = false;
		if(arr && arr.length>=0){
			// 有数据
			state.value = 0;
		}else{
			// 出现错误
			state.value = 5;
			return;
		}
		
		if(arr.length < props.pageSize){
			// 数据没有一页的数量，说明到底了
			state.value = 3;
		}
		
		dataList.value.push(...arr)
		if(dataList.value.length == 0){
			// 如果dataList里也一个数据都没有，表示没有数据
			state.value = 4;
		}
	}
	
	// 获取数据列表
	function getData(index) {
		if(index === undefined) return dataList.value;
		else return dataList.value[index];
	}
	// 动态向列表添加数据，head表示头部添加，不更新状态，比如评论向列表添加一条数据
	function addData(arr, head=true){
		if(head){
			dataList.value.unshift(...arr);
		}else{
			dataList.value.push(...arr)
		}
		if(state.value == 4) state.value = 3;
	}
	// 动态删除列表中一个数据，例如删除一条评论
	function updateData(index, data){
		dataList.value.splice(index, 1, data);
		if(state.value == 4) state.value = 3;
	}
	// 动态删除列表中一个数据，例如删除一条评论
	function deleteData(index){
		dataList.value.splice(index, 1);
		if(dataList.value.length == 0) {
			state.value = 4
		}
	}
	
	
	
	let startY = 0
	let moveY = 0
	// 回弹效果
	function coverTouchstart(e) {
		if (!props.pullDown) {
			return
		}
		coverTransition.value = 'transform .1s linear'
		startY = e.touches[0].clientY
	}
	function coverTouchmove(e) {
		// 取消事件的默认动作，通知 Web 浏览器不要执行与事件关联的默认动作，不加浏览器下拉会触发整个页面下拉
		if(props.fixed) e.preventDefault();
		// 关闭下拉，以及加载中、更新中时不执行
		if (!props.pullDown || state.value == 1 || state.value == 2) {
			return
		}
		moveY = e.touches[0].clientY
		let moveDistance = moveY - startY
		// 上拉不触发
		if (moveDistance < 0) return
		// 下拉
		if (moveDistance <= 50) {
			coverTransform.value = `translateY(${moveDistance}px)`
		}
		moving.value = moveDistance >= 50
	}
	function coverTouchend() {
		if (!props.pullDown || state.value == 1 || state.value == 2) {
			return
		}
		if (moving.value) {
			runRefresh()
		} else {
			coverTransition.value = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
			coverTransform.value = 'translateY(0px)'
		}
	}
	// 代码内触发下拉刷新
	function runRefresh() {
		// 更新中
		state.value = 2;
		scrollTop.value = 0
		coverTransition.value = 'transform .1s linear'
		coverTransform.value = 'translateY(50px)'
		
		currentPage = 1;
		provide();
	}
	
</script>

<style lang="scss" scoped>
	$color: var(--color);
	
	.load-refresh{
		margin: 0;
		padding: 0;
		width: 100%;
		.cover-container{
			width: 100%;
			margin-top: -100rpx;
			.list{
				width: 100%;
				height: 100%;
				min-height: 600rpx;
				box-sizing: border-box;
				.load-more{
					font-size: 24rpx;
					text-align: center;
					color: #AAAAAA;
					padding: 16rpx;
				}
			}
		}
	}
	
	/* #ifndef MP-WEIXIN */
	.waterfall-flow {
		display: grid;
		grid-template-columns: repeat(var(--col), 1fr);
		grid-template-rows: repeat(auto-fill, 1px);
		grid-auto-rows: 1px;
		grid-column-gap: var(--col-gap);
		padding: var(--padding-top) var(--padding-sides) 30rpx;
		.item {
			margin-bottom: var(--row-gap);
			background-color: #fff;
			overflow: hidden;
			// 骗系统开启硬件加速
			transform: transition3d(0, 0, 0);
		}
		.shadow{
			box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.1);
		}
	}
	/* #endif */
	/* #ifdef MP-WEIXIN */
	.waterfall-flow {
		padding: var(--padding-top) var(--padding-sides) 30rpx;
		.item {
			background-color: #fff;
			overflow: hidden;
			// 骗系统开启硬件加速
			transform: transition3d(0, 0, 0);
		}
		.shadow{
			box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.1);
		}
	}
	/* #endif */
	
	/* 动画 */
	.animation {
		width: 100%;
		height: 100rpx;
		color: $color;
		.remind {
			width: 100%;
			height: 100rpx;
			text-align: center;
			line-height: 100rpx;
			font-size: 28rpx;
			color: $color;
		}
		.refresh {
			width: 100%;
			height: 100rpx;
			display: flex;
			align-items: center;
			justify-content: center;
			box-sizing: border-box;
			view {
				// animation-play-state: $playState!important;
			}
		}
	}
</style>
